Practical Monads

https://github.com/jcouyang

ToC

  • Monad
  • Promise
  • Observable
  • Demo

Monad

A monad is just a monoid in the category of endofunctors, what's the problem?

Before Monad

ES6 101

EcmaScript5

  function(x){return x+1}

EcmaScript6

  x=>x+1

Haskell 101

  --  HM(Hindley-Milner) type signature
  liftM :: (Monad m) => (a -> b) -> m a -> m b 
  --        ^type restrict ^function    ^  ^return
  --                              ^curry^   

Functional 101

  [1,2,3].map(x=>x+1)
  // => [2,3,4]
  [1,2,3].filter(x=>x/2==0)
  // => [2]
  [1,2,3].reduce((acc,n)=>acc+n)
  // => 6

Functional 102

  1. Method -> Function
  2. First Class Function
  let toUpperCase = x => x.toUpperCase()  // <- 1
  let map = (f, col) => col.map(f) // <- 1
  map(toUpperCase, ["hello", "world"])
  //    ^2
  //=> ["HELLO", "WORLD"]

Some instances of Functor, Monoid

a Array Functor

  // map:: (String s) => (s -> s) -> [s] -> [s]
  map(toUpperCase, ["hello", "world"])

functor is mapable!

a String Monoid

  "hello" + "world" // <-- concat
  // => "helloworld"
  "hello" + ""   // <--  identity
  // => "hello"
  ("hello" + "world") + "!" == "hello" + ("world" + "!") // <-- associative
  // => true

Monoid is concatable!

  • Functor is mapable!
  • Monoid is concatable!

Substitution

A monad is just a monoid in the category of endofunctors, what's the problem?

  • Catergory -> "Type"
  • Functor -> "Mapable"
  • Monoid -> "Concatable"

"A monad is just a concatable in Type of mapable, what's the problem?"

Concatable in Type

concat

  // concat :: (Monad m) => m m x -> m x
  [1].concat([2]) -> [1,2]
  // concat Array Array -> Array
  T concat T -> T
  type G[X] = T[T[X]]
  G = T compose T = T.T
  T.T[X] = G[X] = T[T[X]]
  concat(T.T).T == T.concat(T.T)

u -> concat

  • u -> concat
  • T(u) -> lift(flat)

pure

  pure(T[X]) == T[T[X]]

n -> pure

  • n -> pure
  • T(n) -> lift(pure)

Promise

lift

  liftM :: (Monad m) => (a -> b) -> m a -> m b 
  const when = require('when')
  // readJSON :: (Promise p, String s, Object o) => (s -> o) -> p s-> p o
  let readJSON = when.lift(JSON.parse)
  readJSON(when('{hello: "world"}'))
      .then(x=>console.log(x))
      .catch(e=>console.error(e.message));
  // => Unexpected token h in JSON at position 1

try

or simply try something and wrap result in Promise

  try(JSON.parse, '{hello: "world"}')
        .then(x=>console.log(x))
        .catch(e=>console.error(e.message));
  // => Unexpected token h in JSON at position 1

fold

  foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
  // reduce :: (Promise p, Number n, Object o) => [n] -> (o -> n -> p o) -> o -> p o
  when.reduce([1,2,3], (acc, n) => rest(URL + n).then(o=>merge(acc, o)), 0)

callback hell

  var allRes = {}
  $.get('url1', (res1) =>
        $.get('url2', (res2) =>
              $.get('url3', (res3) =>
                    allRes = merge(res1, res2, res3))
             )
       )

flatMap

  let futureWorld = new Promise(resolve=>{
      setTimeout(()=>resolve("world"), 1000)
  })

  futureWorld
      .then(world=>new Promise(resolve=>{
          setTimeout(()=>resolve("hello"+world), 1000)
      }))
      .then(x=>console.log(x))
  // 2secs later => helloworld
  • first then is flatMap
  • second then is map

Observable

Space

imperative

  var acc = 0
  for(var n of [1,2,3,4]) {
      acc +=n
  }

functional

  [1,2,3,4].reduce((acc,x)=>acc+x)

imperative accumulate value on time

  var acc = 0;
  $('input').onChange(_=>acc+=_)

Space -> Time

FRP(Functional Reactive Programming)

Single Item Multiple Items
synchronous getData():T getData():List[T]
asynchronous getData():Future[T] getData():Observable[T]

ReactiveX

fold

flatMap

fromEvent

Demo

Fin

Thanks

One more thing

We are hiring

@MilhouseVanHouten